
`define CIRCLE_RADIUS 5

`define REG_LCD_WINDOW_LT 3'd0
`define REG_LCD_WINDOW_RB 3'd1
`define REG_LCD_CIRCLE_LT 3'd2
`define REG_LCD_RECT_LT   3'd3
`define REG_LCD_RECT_RB   3'd4
`define REG_LCD_COLOR     3'd5
`define REG_LCD_START     3'd6

module apb_lcd #(
    parameter APB_ADDR_WIDTH = 12
)(
    input                         HCLK,
    input                         HRESETn,
    input  [APB_ADDR_WIDTH - 1:0] PADDR,
    input  [31:0]                 PWDATA,
    input                         PWRITE,
    input                         PSEL,
    input                         PENABLE,
    output [31:0]                 PRDATA,
    output                        PREADY,
    output                        PSLVERR,

    // lcd
    input                       lcd_clk,
    output                      lcd_cs_n,
    output                      lcd_rs, // 0是命令，1是数据
    output                      lcd_wr_n,
    output                      lcd_rd_n,
    output                      lcd_rst_n,
    inout [15:0]                lcd_db,
    output                      lcd_bl // 背光控制信号(1开启)
);

    logic [31:0] window_lt;
    logic [31:0] window_rb;
    logic [31:0] circle_lt; // 左上角坐标
    logic [31:0] circle_rb;
    logic [31:0] rect_lt;
    logic [31:0] rect_rb;
    logic [15:0] color;
    logic start;
    logic busy, busy_s1, busy_s2;

    assign PSLVERR = 1'b0;
    assign PREADY = 1'b1;
    wire we = PENABLE & PSEL & PWRITE;
    wire [15:0] circle_xe = PWDATA[15:0] + 2 * `CIRCLE_RADIUS + 1;
    wire [15:0] circle_ye = PWDATA[31:16] + 2 * `CIRCLE_RADIUS + 1;

    always_ff @(posedge HCLK)begin
        busy_s1 <= busy;
        busy_s2 <= busy_s1;
        if(~HRESETn)begin
            window_lt <= 0;
            window_rb <= {16'd800, 16'd480};
            circle_lt <= 0;
            circle_rb <= 0;
            rect_lt   <= 0;
            rect_rb   <= 0;
            start     <= 0;
            color     <= 0;
        end
        else begin
            if(busy_s2)begin
                start <= 1'b0;
            end
            if(we)begin
                case(PADDR[4:2])
                3'd0: window_lt <= PWDATA;
                3'd1: window_rb <= PWDATA;
                3'd2: begin
                    circle_lt <= PWDATA;
                    circle_rb <= {circle_ye, circle_xe};
                end
                3'd3: rect_lt <= PWDATA;
                3'd4: rect_rb <= PWDATA;
                3'd5: color   <= PWDATA[15:0];
                3'd6: start   <= PWDATA[0];
                endcase
            end
        end
    end

    logic [31:0] rdata;
    assign PRDATA = rdata;
    always_comb begin
        case(PADDR[4:2])
        3'd0: rdata = window_lt;
        3'd1: rdata = window_rb;
        3'd2: rdata = circle_lt;
        3'd3: rdata = rect_lt;
        3'd4: rdata = rect_rb;
        3'd5: rdata = {16'b0, color};
        3'd6: rdata = {31'b0, busy_s2};
        default: rdata = 0;
        endcase
    end

    logic [15:0] lcd_wdata, lcd_db_r;
    logic lcd_wr_o, lcd_rs_o, lcd_wr_o_n;
    logic lcd_wr_r, lcd_rs_r;
    logic lcd_clk_divide, lcd_clk_2, lcd_clk_2_n;

    assign lcd_cs_n = 1'b0;
    assign lcd_bl   = 1'b1;
    assign lcd_db   = lcd_wr_o_n ? {16{1'bz}} : lcd_db_r;
    assign lcd_wr_n = lcd_wr_r;
    assign lcd_rs   = lcd_rs_r;

    always_ff @(posedge lcd_clk)begin
        lcd_clk_2 <= lcd_clk_divide;
        lcd_clk_2_n <= ~lcd_clk_divide;
        lcd_wr_o_n <= lcd_wr_o;
        if(!HRESETn)begin
            lcd_clk_divide <= 1'b0;
            lcd_db_r <= 0;
            lcd_wr_r <= 1'b1;
            lcd_rs_r <= 1'b0;
        end
        else begin
            lcd_clk_divide <= ~lcd_clk_divide;
            lcd_db_r <= lcd_wdata;
            lcd_wr_r <= ~lcd_wr_o ? lcd_clk_2_n : 1'b1;
            lcd_rs_r <= lcd_rs_o;
        end
    end

    lcd_handle handle (
        .clk(lcd_clk_2),
        .rst_n(HRESETn),
        .xs(window_lt[15:0]),
        .xe(window_rb[15:0]),
        .ys(window_lt[31:16]),
        .ye(window_rb[31:16]),
        .color(color),
        .start(start),
        .cir_xs(circle_lt[15:0]),
        .cir_xe(circle_rb[15:0]),
        .cir_ys(circle_lt[31:16]),
        .cir_ye(circle_rb[31:16]),
        .rect_xs(rect_lt[15:0]),
        .rect_xe(rect_rb[15:0]),
        .rect_ys(rect_lt[31:16]),
        .rect_ye(rect_rb[31:16]),
        .lcd_rst_n(lcd_rst_n),
        .lcd_rs(lcd_rs_o),
        .lcd_wr_n(lcd_wr_o),
        .lcd_rd_n(lcd_rd_n),
        .lcd_db(lcd_wdata),
        .busy(busy)
    );

endmodule